package com.autumn.platform.auth.boot;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import com.autumn.platform.auth.boot.AuthAutoConfiguration.AccessAutoConfiguration;
import com.autumn.platform.auth.core.access.impl.AccessControllerComposite;
import com.autumn.platform.auth.core.access.impl.BaseAccessController;
import com.autumn.platform.auth.core.access.impl.PermissionMappingComposite;
import com.autumn.platform.auth.core.authc.impl.AuthenticatorComposite;
import com.autumn.platform.auth.core.authc.impl.BaseAuthenticator;
import com.autumn.platform.auth.core.authc.impl.PermissionProviderListener;
import com.autumn.platform.auth.web.AuthHandlerMapping;
import com.autumn.platform.auth.web.access.AllowPathMatcherPermissionMapping;
import com.autumn.platform.auth.web.access.HandlerMethodPermissionMapping;
import com.autumn.platform.auth.web.access.HttpOptionsPermissionMapping;
import com.autumn.platform.auth.web.access.IHandlerMethodPermissionMapping;
import com.autumn.platform.web.boot.WebAutoConfiguration;

@Configuration
@EnableConfigurationProperties(AuthProperties.class)
@AutoConfigureAfter(WebAutoConfiguration.class)
@Import({AccessAutoConfiguration.class})
public class AuthAutoConfiguration {

    @Primary
    @Bean
    @ConditionalOnMissingBean(AuthenticatorComposite.class)
    public AuthenticatorComposite authenticatorComposite() {
        return new AuthenticatorComposite();
    }

    @Order(Ordered.LOWEST_PRECEDENCE)
    @Bean
    @ConditionalOnMissingBean(BaseAuthenticator.class)
    public BaseAuthenticator baseAuthenticator() {
        return new BaseAuthenticator();
    }

    @Primary
    @Bean
    @ConditionalOnMissingBean(AccessControllerComposite.class)
    public AccessControllerComposite accessControllerComposite() {
        return new AccessControllerComposite();
    }

    @Order(Ordered.LOWEST_PRECEDENCE)
    @Bean
    @ConditionalOnMissingBean(BaseAccessController.class)
    public BaseAccessController baseAccessController() {
        return new BaseAccessController();
    }

    @Primary
    @Bean
    @ConditionalOnMissingBean(PermissionMappingComposite.class)
    public PermissionMappingComposite permissionMappingComposite() {
        return new PermissionMappingComposite();
    }

    @Bean
    @ConditionalOnMissingBean(PermissionProviderListener.class)
    public PermissionProviderListener permissionProviderListener() {
        return new PermissionProviderListener();
    }

    @Configuration
    @ConditionalOnProperty(prefix = AuthProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
    static class AccessAutoConfiguration {

        private final AuthProperties properties;

        public AccessAutoConfiguration(AuthProperties properties) {
            super();
            this.properties = properties;
        }

        @Bean
        @ConditionalOnMissingBean(AuthHandlerMapping.class)
        public AuthHandlerMapping authHandlerMapping() {
            return new AuthHandlerMapping();
        }

        @Order(Ordered.HIGHEST_PRECEDENCE)
        @Bean
        @ConditionalOnMissingBean(HttpOptionsPermissionMapping.class)
        public HttpOptionsPermissionMapping httpOptionsPermissionMapping() {
            return new HttpOptionsPermissionMapping();
        }

        @Order(Ordered.HIGHEST_PRECEDENCE)
        @Bean
        @ConditionalOnProperty(prefix = AuthProperties.PREFIX, name = "ignore-url-patterns", matchIfMissing = false)
        public AllowPathMatcherPermissionMapping allowPathMatcherPermissionMapping() {
            AllowPathMatcherPermissionMapping mapping = new AllowPathMatcherPermissionMapping();
            String urlPatterns = properties.getIgnoreUrlPatterns();
            String[] patterns = urlPatterns.split("\\s*,\\s*");
            mapping.setUrlPatterns(Arrays.asList(patterns));
            return mapping;
        }

        @Order(Ordered.HIGHEST_PRECEDENCE)
        @Bean
        @ConditionalOnMissingBean(HandlerMethodPermissionMapping.class)
        public HandlerMethodPermissionMapping handlerMethodPermissionMapping(@Autowired(required = false) List<IHandlerMethodPermissionMapping> mappings) {
            HandlerMethodPermissionMapping hmpm = new HandlerMethodPermissionMapping();
            hmpm.setMappings(mappings);
            return hmpm;
        }
    }
}
